/*!
  * Date picker for pickadate.js v3.5.0
  * http://amsul.github.io/pickadate.js/date.htm
  */

(function (factory) {
  factory(Materialize.Picker, jQuery);
})(function (Picker, $) {

  /**
   * Globals and constants
   */
  var DAYS_IN_WEEK = 7,
    WEEKS_IN_CALENDAR = 6,
    _ = Picker._;

  /**
   * The date picker constructor
   */
  function DatePicker(picker, settings) {

    var calendar = this,
      element = picker.$node[0],
      elementValue = element.value,
      elementDataValue = picker.$node.data('value'),
      valueString = elementDataValue || elementValue,
      formatString = elementDataValue ? settings.formatSubmit : settings.format,
      isRTL = function () {

        return element.currentStyle ?

          // For IE.
          element.currentStyle.direction == 'rtl' :

          // For normal browsers.
          getComputedStyle(picker.$root[0]).direction == 'rtl';
      };

    calendar.settings = settings;
    calendar.$node = picker.$node;

    // The queue of methods that will be used to build item objects.
    calendar.queue = {
      min: 'measure create',
      max: 'measure create',
      now: 'now create',
      select: 'parse create validate',
      highlight: 'parse navigate create validate',
      view: 'parse create validate viewset',
      disable: 'deactivate',
      enable: 'activate'

      // The component's item object.
    };
    calendar.item = {};

    calendar.item.clear = null;
    calendar.item.disable = (settings.disable || []).slice(0);
    calendar.item.enable = -function (collectionDisabled) {
      return collectionDisabled[0] === true ? collectionDisabled.shift() : -1;
    }(calendar.item.disable);

    calendar.set('min', settings.min).set('max', settings.max).set('now');

    // When there’s a value, set the `select`, which in turn
    // also sets the `highlight` and `view`.
    if (valueString) {
      calendar.set('select', valueString, {format: formatString});
    }

    // If there’s no value, default to highlighting “today”.
    else {
      calendar.set('select', null).set('highlight', calendar.item.now);
    }

    // The keycode to movement mapping.
    calendar.key = {
      40: 7, // Down
      38: -7, // Up
      39: function () {
        return isRTL() ? -1 : 1;
      }, // Right
      37: function () {
        return isRTL() ? 1 : -1;
      }, // Left
      go: function (timeChange) {
        var highlightedObject = calendar.item.highlight,
          targetDate = new Date(highlightedObject.year, highlightedObject.month, highlightedObject.date + timeChange);
        calendar.set('highlight', targetDate, {interval: timeChange});
        this.render();
      }

      // Bind some picker events.
    };
    picker.on('render', function () {
      picker.$root.find('.' + settings.klass.selectMonth).on('change', function () {
        var value = this.value;
        if (value) {
          picker.set('highlight', [picker.get('view').year, value, picker.get('highlight').date]);
          picker.$root.find('.' + settings.klass.selectMonth).trigger('focus');
        }
      });
      picker.$root.find('.' + settings.klass.selectYear).on('change', function () {
        var value = this.value;
        if (value) {
          picker.set('highlight', [value, picker.get('view').month, picker.get('highlight').date]);
          picker.$root.find('.' + settings.klass.selectYear).trigger('focus');
        }
      });
    }, 1).on('open', function () {
      var includeToday = '';
      if (calendar.disabled(calendar.get('now'))) {
        includeToday = ':not(.' + settings.klass.buttonToday + ')';
      }
      picker.$root.find('button' + includeToday + ', select').attr('disabled', false);
    }, 1).on('close', function () {
      picker.$root.find('button, select').attr('disabled', true);
    }, 1);
  } //DatePicker


  /**
   * Set a datepicker item object.
   */
  DatePicker.prototype.set = function (type, value, options) {

    var calendar = this,
      calendarItem = calendar.item;

    // If the value is `null` just set it immediately.
    if (value === null) {
      if (type == 'clear') type = 'select';
      calendarItem[type] = value;
      return calendar;
    }

    // Otherwise go through the queue of methods, and invoke the functions.
    // Update this as the time unit, and set the final value as this item.
    // * In the case of `enable`, keep the queue but set `disable` instead.
    //   And in the case of `flip`, keep the queue but set `enable` instead.
    calendarItem[type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type] = calendar.queue[type].split(' ').map(function (method) {
      value = calendar[method](type, value, options);
      return value;
    }).pop();

    // Check if we need to cascade through more updates.
    if (type == 'select') {
      calendar.set('highlight', calendarItem.select, options);
    } else if (type == 'highlight') {
      calendar.set('view', calendarItem.highlight, options);
    } else if (type.match(/^(flip|min|max|disable|enable)$/)) {
      if (calendarItem.select && calendar.disabled(calendarItem.select)) {
        calendar.set('select', calendarItem.select, options);
      }
      if (calendarItem.highlight && calendar.disabled(calendarItem.highlight)) {
        calendar.set('highlight', calendarItem.highlight, options);
      }
    }

    return calendar;
  }; //DatePicker.prototype.set


  /**
   * Get a datepicker item object.
   */
  DatePicker.prototype.get = function (type) {
    return this.item[type];
  }; //DatePicker.prototype.get


  /**
   * Create a picker date object.
   */
  DatePicker.prototype.create = function (type, value, options) {

    var isInfiniteValue,
      calendar = this;

    // If there’s no value, use the type as the value.
    value = value === undefined ? type : value;

    // If it’s infinity, update the value.
    if (value == -Infinity || value == Infinity) {
      isInfiniteValue = value;
    }

    // If it’s an object, use the native date object.
    else if ($.isPlainObject(value) && _.isInteger(value.pick)) {
      value = value.obj;
    }

    // If it’s an array, convert it into a date and make sure
    // that it’s a valid date – otherwise default to today.
    else if ($.isArray(value)) {
      value = new Date(value[0], value[1], value[2]);
      value = _.isDate(value) ? value : calendar.create().obj;
    }

    // If it’s a number or date object, make a normalized date.
    else if (_.isInteger(value) || _.isDate(value)) {
      value = calendar.normalize(new Date(value), options);
    }

    // If it’s a literal true or any other case, set it to now.
    else /*if ( value === true )*/{
      value = calendar.now(type, value, options);
    }

    // Return the compiled object.
    return {
      year: isInfiniteValue || value.getFullYear(),
      month: isInfiniteValue || value.getMonth(),
      date: isInfiniteValue || value.getDate(),
      day: isInfiniteValue || value.getDay(),
      obj: isInfiniteValue || value,
      pick: isInfiniteValue || value.getTime()
    };
  }; //DatePicker.prototype.create


  /**
   * Create a range limit object using an array, date object,
   * literal “true”, or integer relative to another time.
   */
  DatePicker.prototype.createRange = function (from, to) {

    var calendar = this,
      createDate = function (date) {
        if (date === true || $.isArray(date) || _.isDate(date)) {
          return calendar.create(date);
        }
        return date;
      };

    // Create objects if possible.
    if (!_.isInteger(from)) {
      from = createDate(from);
    }
    if (!_.isInteger(to)) {
      to = createDate(to);
    }

    // Create relative dates.
    if (_.isInteger(from) && $.isPlainObject(to)) {
      from = [to.year, to.month, to.date + from];
    } else if (_.isInteger(to) && $.isPlainObject(from)) {
      to = [from.year, from.month, from.date + to];
    }

    return {
      from: createDate(from),
      to: createDate(to)
    };
  }; //DatePicker.prototype.createRange


  /**
   * Check if a date unit falls within a date range object.
   */
  DatePicker.prototype.withinRange = function (range, dateUnit) {
    range = this.createRange(range.from, range.to);
    return dateUnit.pick >= range.from.pick && dateUnit.pick <= range.to.pick;
  };

  /**
   * Check if two date range objects overlap.
   */
  DatePicker.prototype.overlapRanges = function (one, two) {

    var calendar = this;

    // Convert the ranges into comparable dates.
    one = calendar.createRange(one.from, one.to);
    two = calendar.createRange(two.from, two.to);

    return calendar.withinRange(one, two.from) || calendar.withinRange(one, two.to) || calendar.withinRange(two, one.from) || calendar.withinRange(two, one.to);
  };

  /**
   * Get the date today.
   */
  DatePicker.prototype.now = function (type, value, options) {
    value = new Date();
    if (options && options.rel) {
      value.setDate(value.getDate() + options.rel);
    }
    return this.normalize(value, options);
  };

  /**
   * Navigate to next/prev month.
   */
  DatePicker.prototype.navigate = function (type, value, options) {

    var targetDateObject,
      targetYear,
      targetMonth,
      targetDate,
      isTargetArray = $.isArray(value),
      isTargetObject = $.isPlainObject(value),
      viewsetObject = this.item.view;
    /*,
                                    safety = 100*/

    if (isTargetArray || isTargetObject) {

      if (isTargetObject) {
        targetYear = value.year;
        targetMonth = value.month;
        targetDate = value.date;
      } else {
        targetYear = +value[0];
        targetMonth = +value[1];
        targetDate = +value[2];
      }

      // If we’re navigating months but the view is in a different
      // month, navigate to the view’s year and month.
      if (options && options.nav && viewsetObject && viewsetObject.month !== targetMonth) {
        targetYear = viewsetObject.year;
        targetMonth = viewsetObject.month;
      }

      // Figure out the expected target year and month.
      targetDateObject = new Date(targetYear, targetMonth + (options && options.nav ? options.nav : 0), 1);
      targetYear = targetDateObject.getFullYear();
      targetMonth = targetDateObject.getMonth();

      // If the month we’re going to doesn’t have enough days,
      // keep decreasing the date until we reach the month’s last date.
      while (/*safety &&*/new Date(targetYear, targetMonth, targetDate).getMonth() !== targetMonth) {
        targetDate -= 1;
        /*safety -= 1
if ( !safety ) {
    throw 'Fell into an infinite loop while navigating to ' + new Date( targetYear, targetMonth, targetDate ) + '.'
}*/
      }

      value = [targetYear, targetMonth, targetDate];
    }

    return value;
  }; //DatePicker.prototype.navigate


  /**
   * Normalize a date by setting the hours to midnight.
   */
  DatePicker.prototype.normalize = function (value /*, options*/) {
    value.setHours(0, 0, 0, 0);
    return value;
  };

  /**
   * Measure the range of dates.
   */
  DatePicker.prototype.measure = function (type, value /*, options*/) {

    var calendar = this;

    // If it’s anything false-y, remove the limits.
    if (!value) {
      value = type == 'min' ? -Infinity : Infinity;
    }

    // If it’s a string, parse it.
    else if (typeof value == 'string') {
      value = calendar.parse(type, value);
    }

    // If it's an integer, get a date relative to today.
    else if (_.isInteger(value)) {
      value = calendar.now(type, value, {rel: value});
    }

    return value;
  }; ///DatePicker.prototype.measure


  /**
   * Create a viewset object based on navigation.
   */
  DatePicker.prototype.viewset = function (type, dateObject /*, options*/) {
    return this.create([dateObject.year, dateObject.month, 1]);
  };

  /**
   * Validate a date as enabled and shift if needed.
   */
  DatePicker.prototype.validate = function (type, dateObject, options) {

    var calendar = this,


      // Keep a reference to the original date.
      originalDateObject = dateObject,


      // Make sure we have an interval.
      interval = options && options.interval ? options.interval : 1,


      // Check if the calendar enabled dates are inverted.
      isFlippedBase = calendar.item.enable === -1,


      // Check if we have any enabled dates after/before now.
      hasEnabledBeforeTarget,
      hasEnabledAfterTarget,


      // The min & max limits.
      minLimitObject = calendar.item.min,
      maxLimitObject = calendar.item.max,


      // Check if we’ve reached the limit during shifting.
      reachedMin,
      reachedMax,


      // Check if the calendar is inverted and at least one weekday is enabled.
      hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter(function (value) {

        // If there’s a date, check where it is relative to the target.
        if ($.isArray(value)) {
          var dateTime = calendar.create(value).pick;
          if (dateTime < dateObject.pick) hasEnabledBeforeTarget = true; else if (dateTime > dateObject.pick) hasEnabledAfterTarget = true;
        }

        // Return only integers for enabled weekdays.
        return _.isInteger(value);
      }).length;
    /*,
           safety = 100*/

    // Cases to validate for:
    // [1] Not inverted and date disabled.
    // [2] Inverted and some dates enabled.
    // [3] Not inverted and out of range.
    //
    // Cases to **not** validate for:
    // • Navigating months.
    // • Not inverted and date enabled.
    // • Inverted and all dates disabled.
    // • ..and anything else.
    if (!options || !options.nav) if (
      /* 1 */!isFlippedBase && calendar.disabled(dateObject) ||
    /* 2 */isFlippedBase && calendar.disabled(dateObject) && (hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget) ||
    /* 3 */!isFlippedBase && (dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick)) {

      // When inverted, flip the direction if there aren’t any enabled weekdays
      // and there are no enabled dates in the direction of the interval.
      if (isFlippedBase && !hasEnabledWeekdays && (!hasEnabledAfterTarget && interval > 0 || !hasEnabledBeforeTarget && interval < 0)) {
        interval *= -1;
      }

      // Keep looping until we reach an enabled date.
      while (/*safety &&*/calendar.disabled(dateObject)) {

        /*safety -= 1
if ( !safety ) {
    throw 'Fell into an infinite loop while validating ' + dateObject.obj + '.'
}*/

        // If we’ve looped into the next/prev month with a large interval, return to the original date and flatten the interval.
        if (Math.abs(interval) > 1 && (dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month)) {
          dateObject = originalDateObject;
          interval = interval > 0 ? 1 : -1;
        }

        // If we’ve reached the min/max limit, reverse the direction, flatten the interval and set it to the limit.
        if (dateObject.pick <= minLimitObject.pick) {
          reachedMin = true;
          interval = 1;
          dateObject = calendar.create([minLimitObject.year, minLimitObject.month, minLimitObject.date + (dateObject.pick === minLimitObject.pick ? 0 : -1)]);
        } else if (dateObject.pick >= maxLimitObject.pick) {
          reachedMax = true;
          interval = -1;
          dateObject = calendar.create([maxLimitObject.year, maxLimitObject.month, maxLimitObject.date + (dateObject.pick === maxLimitObject.pick ? 0 : 1)]);
        }

        // If we’ve reached both limits, just break out of the loop.
        if (reachedMin && reachedMax) {
          break;
        }

        // Finally, create the shifted date using the interval and keep looping.
        dateObject = calendar.create([dateObject.year, dateObject.month, dateObject.date + interval]);
      }
    } //endif


    // Return the date object settled on.
    return dateObject;
  }; //DatePicker.prototype.validate


  /**
   * Check if a date is disabled.
   */
  DatePicker.prototype.disabled = function (dateToVerify) {

    var calendar = this,


      // Filter through the disabled dates to check if this is one.
      isDisabledMatch = calendar.item.disable.filter(function (dateToDisable) {

        // If the date is a number, match the weekday with 0index and `firstDay` check.
        if (_.isInteger(dateToDisable)) {
          return dateToVerify.day === (calendar.settings.firstDay ? dateToDisable : dateToDisable - 1) % 7;
        }

        // If it’s an array or a native JS date, create and match the exact date.
        if ($.isArray(dateToDisable) || _.isDate(dateToDisable)) {
          return dateToVerify.pick === calendar.create(dateToDisable).pick;
        }

        // If it’s an object, match a date within the “from” and “to” range.
        if ($.isPlainObject(dateToDisable)) {
          return calendar.withinRange(dateToDisable, dateToVerify);
        }
      });

    // If this date matches a disabled date, confirm it’s not inverted.
    isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function (dateToDisable) {
      return $.isArray(dateToDisable) && dateToDisable[3] == 'inverted' || $.isPlainObject(dateToDisable) && dateToDisable.inverted;
    }).length;

    // Check the calendar “enabled” flag and respectively flip the
    // disabled state. Then also check if it’s beyond the min/max limits.
    return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch || dateToVerify.pick < calendar.item.min.pick || dateToVerify.pick > calendar.item.max.pick;
  }; //DatePicker.prototype.disabled


  /**
   * Parse a string into a usable type.
   */
  DatePicker.prototype.parse = function (type, value, options) {

    var calendar = this,
      parsingObject = {};

    // If it’s already parsed, we’re good.
    if (!value || typeof value != 'string') {
      return value;
    }

    // We need a `.format` to parse the value with.
    if (!(options && options.format)) {
      options = options || {};
      options.format = calendar.settings.format;
    }

    // Convert the format into an array and then map through it.
    calendar.formats.toArray(options.format).map(function (label) {

      var
        // Grab the formatting label.
        formattingLabel = calendar.formats[label],


        // The format length is from the formatting label function or the
        // label length without the escaping exclamation (!) mark.
        formatLength = formattingLabel ? _.trigger(formattingLabel, calendar, [value, parsingObject]) : label.replace(/^!/, '').length;

      // If there's a format label, split the value up to the format length.
      // Then add it to the parsing object with appropriate label.
      if (formattingLabel) {
        parsingObject[label] = value.substr(0, formatLength);
      }

      // Update the value as the substring from format length to end.
      value = value.substr(formatLength);
    });

    // Compensate for month 0index.
    return [parsingObject.yyyy || parsingObject.yy, +(parsingObject.mm || parsingObject.m) - 1, parsingObject.dd || parsingObject.d];
  }; //DatePicker.prototype.parse


  /**
   * Various formats to display the object in.
   */
  DatePicker.prototype.formats = function () {

    // Return the length of the first word in a collection.
    function getWordLengthFromCollection(string, collection, dateObject) {

      // Grab the first word from the string.
      var word = string.match(/\w+/)[0];

      // If there's no month index, add it to the date object
      if (!dateObject.mm && !dateObject.m) {
        dateObject.m = collection.indexOf(word) + 1;
      }

      // Return the length of the word.
      return word.length;
    }

    // Get the length of the first word in a string.
    function getFirstWordLength(string) {
      return string.match(/\w+/)[0].length;
    }

    return {

      d: function (string, dateObject) {

        // If there's string, then get the digits length.
        // Otherwise return the selected date.
        return string ? _.digits(string) : dateObject.date;
      },
      dd: function (string, dateObject) {

        // If there's a string, then the length is always 2.
        // Otherwise return the selected date with a leading zero.
        return string ? 2 : _.lead(dateObject.date);
      },
      ddd: function (string, dateObject) {

        // If there's a string, then get the length of the first word.
        // Otherwise return the short selected weekday.
        return string ? getFirstWordLength(string) : this.settings.weekdaysShort[dateObject.day];
      },
      dddd: function (string, dateObject) {

        // If there's a string, then get the length of the first word.
        // Otherwise return the full selected weekday.
        return string ? getFirstWordLength(string) : this.settings.weekdaysFull[dateObject.day];
      },
      m: function (string, dateObject) {

        // If there's a string, then get the length of the digits
        // Otherwise return the selected month with 0index compensation.
        return string ? _.digits(string) : dateObject.month + 1;
      },
      mm: function (string, dateObject) {

        // If there's a string, then the length is always 2.
        // Otherwise return the selected month with 0index and leading zero.
        return string ? 2 : _.lead(dateObject.month + 1);
      },
      mmm: function (string, dateObject) {

        var collection = this.settings.monthsShort;

        // If there's a string, get length of the relevant month from the short
        // months collection. Otherwise return the selected month from that collection.
        return string ? getWordLengthFromCollection(string, collection, dateObject) : collection[dateObject.month];
      },
      mmmm: function (string, dateObject) {

        var collection = this.settings.monthsFull;

        // If there's a string, get length of the relevant month from the full
        // months collection. Otherwise return the selected month from that collection.
        return string ? getWordLengthFromCollection(string, collection, dateObject) : collection[dateObject.month];
      },
      yy: function (string, dateObject) {

        // If there's a string, then the length is always 2.
        // Otherwise return the selected year by slicing out the first 2 digits.
        return string ? 2 : ('' + dateObject.year).slice(2);
      },
      yyyy: function (string, dateObject) {

        // If there's a string, then the length is always 4.
        // Otherwise return the selected year.
        return string ? 4 : dateObject.year;
      },

      // Create an array by splitting the formatting string passed.
      toArray: function (formatString) {
        return formatString.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g);
      },

      // Format an object into a string using the formatting options.
      toString: function (formatString, itemObject) {
        var calendar = this;
        return calendar.formats.toArray(formatString).map(function (label) {
          return _.trigger(calendar.formats[label], calendar, [0, itemObject]) || label.replace(/^!/, '');
        }).join('');
      }
    };
  }(); //DatePicker.prototype.formats


  /**
   * Check if two date units are the exact.
   */
  DatePicker.prototype.isDateExact = function (one, two) {

    var calendar = this;

    // When we’re working with weekdays, do a direct comparison.
    if (_.isInteger(one) && _.isInteger(two) || typeof one == 'boolean' && typeof two == 'boolean') {
      return one === two;
    }

    // When we’re working with date representations, compare the “pick” value.
    if ((_.isDate(one) || $.isArray(one)) && (_.isDate(two) || $.isArray(two))) {
      return calendar.create(one).pick === calendar.create(two).pick;
    }

    // When we’re working with range objects, compare the “from” and “to”.
    if ($.isPlainObject(one) && $.isPlainObject(two)) {
      return calendar.isDateExact(one.from, two.from) && calendar.isDateExact(one.to, two.to);
    }

    return false;
  };

  /**
   * Check if two date units overlap.
   */
  DatePicker.prototype.isDateOverlap = function (one, two) {

    var calendar = this,
      firstDay = calendar.settings.firstDay ? 1 : 0;

    // When we’re working with a weekday index, compare the days.
    if (_.isInteger(one) && (_.isDate(two) || $.isArray(two))) {
      one = one % 7 + firstDay;
      return one === calendar.create(two).day + 1;
    }
    if (_.isInteger(two) && (_.isDate(one) || $.isArray(one))) {
      two = two % 7 + firstDay;
      return two === calendar.create(one).day + 1;
    }

    // When we’re working with range objects, check if the ranges overlap.
    if ($.isPlainObject(one) && $.isPlainObject(two)) {
      return calendar.overlapRanges(one, two);
    }

    return false;
  };

  /**
   * Flip the “enabled” state.
   */
  DatePicker.prototype.flipEnable = function (val) {
    var itemObject = this.item;
    itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1);
  };

  /**
   * Mark a collection of dates as “disabled”.
   */
  DatePicker.prototype.deactivate = function (type, datesToDisable) {

    var calendar = this,
      disabledItems = calendar.item.disable.slice(0);

    // If we’re flipping, that’s all we need to do.
    if (datesToDisable == 'flip') {
      calendar.flipEnable();
    } else if (datesToDisable === false) {
      calendar.flipEnable(1);
      disabledItems = [];
    } else if (datesToDisable === true) {
      calendar.flipEnable(-1);
      disabledItems = [];
    }

    // Otherwise go through the dates to disable.
    else {

      datesToDisable.map(function (unitToDisable) {

        var matchFound;

        // When we have disabled items, check for matches.
        // If something is matched, immediately break out.
        for (var index = 0; index < disabledItems.length; index += 1) {
          if (calendar.isDateExact(unitToDisable, disabledItems[index])) {
            matchFound = true;
            break;
          }
        }

        // If nothing was found, add the validated unit to the collection.
        if (!matchFound) {
          if (_.isInteger(unitToDisable) || _.isDate(unitToDisable) || $.isArray(unitToDisable) || $.isPlainObject(unitToDisable) && unitToDisable.from && unitToDisable.to) {
            disabledItems.push(unitToDisable);
          }
        }
      });
    }

    // Return the updated collection.
    return disabledItems;
  }; //DatePicker.prototype.deactivate


  /**
   * Mark a collection of dates as “enabled”.
   */
  DatePicker.prototype.activate = function (type, datesToEnable) {

    var calendar = this,
      disabledItems = calendar.item.disable,
      disabledItemsCount = disabledItems.length;

    // If we’re flipping, that’s all we need to do.
    if (datesToEnable == 'flip') {
      calendar.flipEnable();
    } else if (datesToEnable === true) {
      calendar.flipEnable(1);
      disabledItems = [];
    } else if (datesToEnable === false) {
      calendar.flipEnable(-1);
      disabledItems = [];
    }

    // Otherwise go through the disabled dates.
    else {

      datesToEnable.map(function (unitToEnable) {

        var matchFound, disabledUnit, index, isExactRange;

        // Go through the disabled items and try to find a match.
        for (index = 0; index < disabledItemsCount; index += 1) {

          disabledUnit = disabledItems[index];

          // When an exact match is found, remove it from the collection.
          if (calendar.isDateExact(disabledUnit, unitToEnable)) {
            matchFound = disabledItems[index] = null;
            isExactRange = true;
            break;
          }

          // When an overlapped match is found, add the “inverted” state to it.
          else if (calendar.isDateOverlap(disabledUnit, unitToEnable)) {
            if ($.isPlainObject(unitToEnable)) {
              unitToEnable.inverted = true;
              matchFound = unitToEnable;
            } else if ($.isArray(unitToEnable)) {
              matchFound = unitToEnable;
              if (!matchFound[3]) matchFound.push('inverted');
            } else if (_.isDate(unitToEnable)) {
              matchFound = [unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted'];
            }
            break;
          }
        }

        // If a match was found, remove a previous duplicate entry.
        if (matchFound) for (index = 0; index < disabledItemsCount; index += 1) {
          if (calendar.isDateExact(disabledItems[index], unitToEnable)) {
            disabledItems[index] = null;
            break;
          }
        }

        // In the event that we’re dealing with an exact range of dates,
        // make sure there are no “inverted” dates because of it.
        if (isExactRange) for (index = 0; index < disabledItemsCount; index += 1) {
          if (calendar.isDateOverlap(disabledItems[index], unitToEnable)) {
            disabledItems[index] = null;
            break;
          }
        }

        // If something is still matched, add it into the collection.
        if (matchFound) {
          disabledItems.push(matchFound);
        }
      });
    }

    // Return the updated collection.
    return disabledItems.filter(function (val) {
      return val != null;
    });
  }; //DatePicker.prototype.activate


  /**
   * Create a string for the nodes in the picker.
   */
  DatePicker.prototype.nodes = function (isOpen) {

    var calendar = this,
      settings = calendar.settings,
      calendarItem = calendar.item,
      nowObject = calendarItem.now,
      selectedObject = calendarItem.select,
      highlightedObject = calendarItem.highlight,
      viewsetObject = calendarItem.view,
      disabledCollection = calendarItem.disable,
      minLimitObject = calendarItem.min,
      maxLimitObject = calendarItem.max,


      // Create the calendar table head using a copy of weekday labels collection.
      // * We do a copy so we don't mutate the original array.
      tableHead = function (collection, fullCollection) {

        // If the first day should be Monday, move Sunday to the end.
        if (settings.firstDay) {
          collection.push(collection.shift());
          fullCollection.push(fullCollection.shift());
        }

        // Create and return the table head group.
        return _.node('thead', _.node('tr', _.group({
          min: 0,
          max: DAYS_IN_WEEK - 1,
          i: 1,
          node: 'th',
          item: function (counter) {
            return [collection[counter], settings.klass.weekdays, 'scope=col title="' + fullCollection[counter] + '"'];
          }
        }))); //endreturn

        // Materialize modified
      }((settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysLetter).slice(0), settings.weekdaysFull.slice(0)),
      //tableHead


      // Create the nav for next/prev month.
      createMonthNav = function (next) {

        // Otherwise, return the created month tag.
        return _.node('div', ' ', settings.klass['nav' + (next ? 'Next' : 'Prev')] + (

          // If the focused month is outside the range, disabled the button.
          next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month || !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ? ' ' + settings.klass.navDisabled : ''), 'data-nav=' + (next || -1) + ' ' + _.ariaAttr({
          role: 'button',
          controls: calendar.$node[0].id + '_table'
        }) + ' ' + 'title="' + (next ? settings.labelMonthNext : settings.labelMonthPrev) + '"'); //endreturn
      },
      //createMonthNav


      // Create the month label.
      //Materialize modified
      createMonthLabel = function (override) {

        var monthsCollection = settings.showMonthsShort ? settings.monthsShort : settings.monthsFull;

        // Materialize modified
        if (override == "short_months") {
          monthsCollection = settings.monthsShort;
        }

        // If there are months to select, add a dropdown menu.
        if (settings.selectMonths && override == undefined) {

          return _.node('select', _.group({
            min: 0,
            max: 11,
            i: 1,
            node: 'option',
            item: function (loopedMonth) {

              return [

                // The looped month and no classes.
                monthsCollection[loopedMonth], 0,

                // Set the value and selected index.
                'value=' + loopedMonth + (viewsetObject.month == loopedMonth ? ' selected' : '') + (viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month || viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month ? ' disabled' : '')];
            }
          }), settings.klass.selectMonth + ' browser-default', (isOpen ? '' : 'disabled') + ' ' + _.ariaAttr({controls: calendar.$node[0].id + '_table'}) + ' ' + 'title="' + settings.labelMonthSelect + '"');
        }

        // Materialize modified
        if (override == "short_months") if (selectedObject != null) return monthsCollection[selectedObject.month]; else return monthsCollection[viewsetObject.month];

        // If there's a need for a month selector
        return _.node('div', monthsCollection[viewsetObject.month], settings.klass.month);
      },
      //createMonthLabel


      // Create the year label.
      // Materialize modified
      createYearLabel = function (override) {

        var focusedYear = viewsetObject.year,


          // If years selector is set to a literal "true", set it to 5. Otherwise
          // divide in half to get half before and half after focused year.
          numberYears = settings.selectYears === true ? 5 : ~~(settings.selectYears / 2);

        // If there are years to select, add a dropdown menu.
        if (numberYears) {

          var minYear = minLimitObject.year,
            maxYear = maxLimitObject.year,
            lowestYear = focusedYear - numberYears,
            highestYear = focusedYear + numberYears;

          // If the min year is greater than the lowest year, increase the highest year
          // by the difference and set the lowest year to the min year.
          if (minYear > lowestYear) {
            highestYear += minYear - lowestYear;
            lowestYear = minYear;
          }

          // If the max year is less than the highest year, decrease the lowest year
          // by the lower of the two: available and needed years. Then set the
          // highest year to the max year.
          if (maxYear < highestYear) {

            var availableYears = lowestYear - minYear,
              neededYears = highestYear - maxYear;

            lowestYear -= availableYears > neededYears ? neededYears : availableYears;
            highestYear = maxYear;
          }

          if (settings.selectYears && override == undefined) {
            return _.node('select', _.group({
              min: lowestYear,
              max: highestYear,
              i: 1,
              node: 'option',
              item: function (loopedYear) {
                return [

                  // The looped year and no classes.
                  loopedYear, 0,

                  // Set the value and selected index.
                  'value=' + loopedYear + (focusedYear == loopedYear ? ' selected' : '')];
              }
            }), settings.klass.selectYear + ' browser-default', (isOpen ? '' : 'disabled') + ' ' + _.ariaAttr({controls: calendar.$node[0].id + '_table'}) + ' ' + 'title="' + settings.labelYearSelect + '"');
          }
        }

        // Materialize modified
        if (override === 'raw' && selectedObject != null) {
          return _.node('div', selectedObject.year);
        }

        // Otherwise just return the year focused
        return _.node('div', focusedYear, settings.klass.year);
      }; //createYearLabel


    // Materialize modified
    createDayLabel = function () {
      if (selectedObject != null) return selectedObject.date; else return nowObject.date;
    };
    createWeekdayLabel = function () {
      var display_day;

      if (selectedObject != null) display_day = selectedObject.day; else display_day = nowObject.day;
      var weekday = settings.weekdaysShort[display_day];
      return weekday;
    };

    // Create and return the entire calendar.

    return _.node(
      // Date presentation View
      'div', _.node(
      // Div for Year
      'div', createYearLabel("raw"), settings.klass.year_display) + _.node('span', createWeekdayLabel() + ', ', "picker__weekday-display") + _.node(
      // Div for short Month
      'span', createMonthLabel("short_months") + ' ', settings.klass.month_display) + _.node(
      // Div for Day
      'span', createDayLabel(), settings.klass.day_display), settings.klass.date_display) +
      // Calendar container
      _.node('div', _.node('div', _.node('div', (settings.selectYears ? createMonthLabel() + createYearLabel() : createMonthLabel() + createYearLabel()) + createMonthNav() + createMonthNav(1), settings.klass.header) + _.node('table', tableHead + _.node('tbody', _.group({
          min: 0,
          max: WEEKS_IN_CALENDAR - 1,
          i: 1,
          node: 'tr',
          item: function (rowCounter) {

            // If Monday is the first day and the month starts on Sunday, shift the date back a week.
            var shiftDateBy = settings.firstDay && calendar.create([viewsetObject.year, viewsetObject.month, 1]).day === 0 ? -7 : 0;

            return [_.group({
              min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index
              max: function () {
                return this.min + DAYS_IN_WEEK - 1;
              },
              i: 1,
              node: 'td',
              item: function (targetDate) {

                // Convert the time date from a relative date to a target date.
                targetDate = calendar.create([viewsetObject.year, viewsetObject.month, targetDate + (settings.firstDay ? 1 : 0)]);

                var isSelected = selectedObject && selectedObject.pick == targetDate.pick,
                  isHighlighted = highlightedObject && highlightedObject.pick == targetDate.pick,
                  isDisabled = disabledCollection && calendar.disabled(targetDate) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick,
                  formattedDate = _.trigger(calendar.formats.toString, calendar, [settings.format, targetDate]);

                return [_.node('div', targetDate.date, function (klasses) {

                  // Add the `infocus` or `outfocus` classes based on month in view.
                  klasses.push(viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus);

                  // Add the `today` class if needed.
                  if (nowObject.pick == targetDate.pick) {
                    klasses.push(settings.klass.now);
                  }

                  // Add the `selected` class if something's selected and the time matches.
                  if (isSelected) {
                    klasses.push(settings.klass.selected);
                  }

                  // Add the `highlighted` class if something's highlighted and the time matches.
                  if (isHighlighted) {
                    klasses.push(settings.klass.highlighted);
                  }

                  // Add the `disabled` class if something's disabled and the object matches.
                  if (isDisabled) {
                    klasses.push(settings.klass.disabled);
                  }

                  return klasses.join(' ');
                }([settings.klass.day]), 'data-pick=' + targetDate.pick + ' ' + _.ariaAttr({
                  role: 'gridcell',
                  label: formattedDate,
                  selected: isSelected && calendar.$node.val() === formattedDate ? true : null,
                  activedescendant: isHighlighted ? true : null,
                  disabled: isDisabled ? true : null
                }) + ' ' + (isDisabled ? '' : 'tabindex="0"')), '', _.ariaAttr({role: 'presentation'})]; //endreturn
              }
            })]; //endreturn
          }
        })), settings.klass.table, 'id="' + calendar.$node[0].id + '_table' + '" ' + _.ariaAttr({
          role: 'grid',
          controls: calendar.$node[0].id,
          readonly: true
        })), settings.klass.calendar_container) // end calendar

        +

        // * For Firefox forms to submit, make sure to set the buttons’ `type` attributes as “button”.
        _.node('div', _.node('button', settings.today, "btn-flat picker__today waves-effect", 'type=button data-pick=' + nowObject.pick + (isOpen && !calendar.disabled(nowObject) ? '' : ' disabled') + ' ' + _.ariaAttr({controls: calendar.$node[0].id})) + _.node('button', settings.clear, "btn-flat picker__clear waves-effect", 'type=button data-clear=1' + (isOpen ? '' : ' disabled') + ' ' + _.ariaAttr({controls: calendar.$node[0].id})) + _.node('button', settings.close, "btn-flat picker__close waves-effect", 'type=button data-close=true ' + (isOpen ? '' : ' disabled') + ' ' + _.ariaAttr({controls: calendar.$node[0].id})), settings.klass.footer), 'picker__container__wrapper'); //endreturn
  }; //DatePicker.prototype.nodes


  /**
   * The date picker defaults.
   */
  DatePicker.defaults = function (prefix) {

    return {

      // The title label to use for the month nav buttons
      labelMonthNext: '下一个月份',
      labelMonthPrev: '上一个月份',

      // The title label to use for the dropdown selectors
      labelMonthSelect: '选择月',
      labelYearSelect: '选择年',

      // Months and weekdays
      monthsFull: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
      monthsShort: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
      weekdaysFull: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
      weekdaysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],

      // Materialize modified
      weekdaysLetter: ['日', '一', '二', '三', '四', '五', '六'],

      // Today and clear
      today: '今天',
      clear: '清除',
      close: '确定',

      selectMonths: true,
      selectYears: 15,
      closeOnSelect: false,

      // The format to show on the `input` element
      format: 'yyyy-mm-dd',

      // Classes
      klass: {
        table: prefix + 'table',
        header: prefix + 'header',

        // Materialize Added klasses
        date_display: prefix + 'date-display',
        month_display: prefix + 'month-display',
        day_display: prefix + 'day-display',
        year_display: prefix + 'year-display',
        calendar_container: prefix + 'calendar-container',
        // end

        navPrev: prefix + 'nav--prev',
        navNext: prefix + 'nav--next',
        navDisabled: prefix + 'nav--disabled',

        month: prefix + 'month',
        year: prefix + 'year',

        selectMonth: prefix + 'select--month',
        selectYear: prefix + 'select--year',

        weekdays: prefix + 'weekday',

        day: prefix + 'day',
        disabled: prefix + 'day--disabled',
        selected: prefix + 'day--selected',
        highlighted: prefix + 'day--highlighted',
        now: prefix + 'day--today',
        infocus: prefix + 'day--infocus',
        outfocus: prefix + 'day--outfocus',

        footer: prefix + 'footer',

        buttonClear: prefix + 'button--clear',
        buttonToday: prefix + 'button--today',
        buttonClose: prefix + 'button--close'
      }
    };
  }(Picker.klasses().picker + '__');

  /**
   * Extend the picker to add the date picker.
   */
  Picker.extend('pickadatee', DatePicker);
});